#include <Core/Commands.h>

namespace Core {

bool sendCommand(SyncedCommandIOStreamPtr stream, ICommandPtr command) 
{
	return (CommandCreateAdapters::pack<ProtobufCommandTranslator>(stream, command));
}

/**
 * Public Google buffer protocol translators api
 */
ICommandPtr ProtobufCommandTranslator::decodeGeneric(
	SyncedCommandIOStreamPtr stream, ProtobufCommandTranslator::TranslateFunc tf) 
{
	ICommandPtr command;
	TranslatorType::MessagePtr message = tf(m_translator, stream);
	if (message) {
		switch (message->type()) {	
			case ::ScanerCommandProto::Command_CommandType_PING :
				command.reset(new PingCommand());
				break;
			
			case ::ScanerCommandProto::Command_CommandType_FILE_SCAN :
				command.reset(new FileScanCommand(message->filescandata().filepath()));
				break;
			
			case ::ScanerCommandProto::Command_CommandType_ARRAY_SCAN :
				command.reset(new ArrayScanCommand(
					message->arrayscandata().chunksize(), message->arrayscandata().arraysize()));
				break;

			case ::ScanerCommandProto::Command_CommandType_GUID :
				command.reset(new GuidReplyCommand(message->guiddata().guid()));
				break;
			
			case ::ScanerCommandProto::Command_CommandType_ERROR :
				command.reset(new ErrorCommand(
					message->externalerrdata().message(), message->externalerrdata().errorcode()));	
				break;
			
			case ::ScanerCommandProto::Command_CommandType_OK :
				command.reset(new OkCommand());
				break;

			default :
				command.reset(new ErrorCommand("Unknown command type.", (int)Aux::UNKNOWN_COMMAND));
				break;
		}
	} // translate to Scaner command
	return command;
}

bool ProtobufCommandTranslator::encode(SyncedCommandIOStreamPtr stream, ICommandPtr command) 
{
	switch (command->getTag()) {
		case FileScanCommand::tag : 
			return fileScanCommandEncode(stream, commandCast<FileScanCommand>(command));

		case ArrayScanCommand::tag : 
			return arrayScanCommandEncode(stream, commandCast<ArrayScanCommand>(command));		
		
		case PingCommand::tag : 
			return pingScanCommandEncode(stream, commandCast<PingCommand>(command));
		
		case OkCommand::tag :
			return okScanCommandEncode(stream, commandCast<OkCommand>(command));
		
		case ErrorCommand::tag :
			return errorCommandEncode(stream, commandCast<ErrorCommand>(command));
		
		case GuidReplyCommand::tag :
			return guidReplyCommandEncode(stream, commandCast<GuidReplyCommand>(command));
		
		default : break;
	}
	return false;
}

/**
 * Private Google buffer protocol translators api
 */

bool ProtobufCommandTranslator::encodeAndWrite(SyncedCommandIOStreamPtr stream) 
{
	Core::Proto::BufferType outBuf;
	if (!m_translator.encodePack(outBuf))
		return false;
	return (stream->write(outBuf) > 0);
}

bool ProtobufCommandTranslator::fileScanCommandEncode(
	SyncedCommandIOStreamPtr stream, FileScanCommandPtr command) 
{
	BOOST_ASSERT(stream != NULL && command != NULL);
	if (stream && command) {
		TranslatorType::MessagePtr message(new TranslatorType::MessageType());
		m_translator.setMessage(message);
		message->set_type(::ScanerCommandProto::Command_CommandType_FILE_SCAN);
		message->mutable_filescandata()->set_filepath(command->getFilePath());
		return encodeAndWrite(stream);
	}
	return false;
}

bool ProtobufCommandTranslator::arrayScanCommandEncode(
	SyncedCommandIOStreamPtr stream, ArrayScanCommandPtr command) 
{
	BOOST_ASSERT(stream != NULL && command != NULL);
	if (stream && command) {
		TranslatorType::MessagePtr message(new TranslatorType::MessageType());
		m_translator.setMessage(message);
		message->set_type(::ScanerCommandProto::Command_CommandType_ARRAY_SCAN);
		message->mutable_arrayscandata()->set_chunksize(command->getChunkSize());
		message->mutable_arrayscandata()->set_arraysize(command->getArraySize());
		return encodeAndWrite(stream);
	}
	return false;
}

bool ProtobufCommandTranslator::pingScanCommandEncode(
	SyncedCommandIOStreamPtr stream, PingCommandPtr command) 
{	
	BOOST_ASSERT(stream != NULL && command != NULL);
	if (stream && command) {
		TranslatorType::MessagePtr  message(new TranslatorType::MessageType());
		m_translator.setMessage(message);
		message->set_type(::ScanerCommandProto::Command_CommandType_PING);	
		return encodeAndWrite(stream);
	}
	return false;
}

bool ProtobufCommandTranslator::okScanCommandEncode(
	SyncedCommandIOStreamPtr stream, OkCommandPtr command) 
{
	BOOST_ASSERT(stream != NULL && command != NULL);
	if (stream && command) {
		TranslatorType::MessagePtr  message(new TranslatorType::MessageType());
		m_translator.setMessage(message);
		message->set_type(::ScanerCommandProto::Command_CommandType_OK);
		return encodeAndWrite(stream);
	}
	return false;
}

bool ProtobufCommandTranslator::errorCommandEncode(
	SyncedCommandIOStreamPtr stream, ErrorCommandPtr command)
{
	BOOST_ASSERT(stream != NULL && command != NULL);
	if (stream && command) {
		TranslatorType::MessagePtr  message(new TranslatorType::MessageType());
		m_translator.setMessage(message);
		message->set_type(::ScanerCommandProto::Command_CommandType_ERROR);
		message->mutable_externalerrdata()->set_message(command->getMessage());
		message->mutable_externalerrdata()->set_errorcode(command->getErrorCode());
		return encodeAndWrite(stream);
	}
	return false;
}

bool ProtobufCommandTranslator::guidReplyCommandEncode(
	SyncedCommandIOStreamPtr stream, GuidReplyCommandPtr command) 
{
	BOOST_ASSERT(stream != NULL && command != NULL);
	if (stream && command) {
		TranslatorType::MessagePtr  message(new TranslatorType::MessageType());
		m_translator.setMessage(message);
		message->set_type(::ScanerCommandProto::Command_CommandType_GUID);
		message->mutable_guiddata()->set_guid(command->getGuid());
		return encodeAndWrite(stream);
	}
	return false;
}

} // namespace Core

